home *** CD-ROM | disk | FTP | other *** search
- /*
- * MICRO-Terminal:
- *
- * This is a very simple communications program, which provides
- * a subset ANSI (VT100) terminal emulation, and basic XMODEM
- * (with checksum) file transfer.
- *
- * If HOTKEYS are specified on the command line, MTERM will install
- * itself as a TSR (Ram-Resident) program, which can be invoked at
- * any time by pressing the HOTKEYS. Available HOTKEYS are:
- * L - Left SHIFT
- * R - Right SHIFT
- * A - ALT
- * C - CONTROL
- * S - SysRq (Caution: some systems may not like this one)
- *
- * EG: mterm LR (Install with LEFT+RIGHT SHIFT for hotkeys)
- *
- * This demonstrates the use of the MICRO-C video interface
- * and communications library functions for the IBM/PC, as well
- * as the "SAVE_VIDEO", "RESTORE_VIDEO" and "TSR" functions.
- *
- * Copyright 1990-1994 Dave Dunfield
- * All rights reserved.
- *
- * Permission granted for personal (non-commercial) use only.
- *
- * Compile command: cc mterm -fop
- */
- #include <stdio.h> /* Standard I/O definitions */
- #include <comm.h> /* Comm I/O definitions */
- #include <video.h> /* Video I/O definitions */
- #include <file.h> /* File I/O definitions */
- #include <tsr.h> /* Tsr function definitions */
-
- /* Screen output positions */
- #define SETROW 3 /* Screen row for settings display */
- #define MSGROW 20 /* Screen row for messages */
- #define MENROW 7 /* Screen row for menu items */
- #define MAICOL 0 /* Screen column for main menu */
- #define SUBCOL 20 /* Screen column for sub menu */
- #define FILCOL 5 /* Screen column for file prompt */
- #define FILSIZ 50 /* Maximum size of file name */
-
- /* XMODEM parameters */
- #define BLOCK_SIZE 128 /* size of transmit blocks */
- #define RETRYS 10 /* maximum number of retrys */
- #define SOH_TIMEOUT 5 /* How long to wait for start of packet */
- #define RX_TIMEOUT 2 /* How long in wait for chars in packet */
- #define ACK_TIMEOUT 5 /* How long to wait for acknowlege */
-
- /* Line control codes */
- #define SOH 0x01 /* start of header */
- #define ACK 0x06 /* Acknowledge */
- #define NAK 0x15 /* Negative acknowledge */
- #define CAN 0x18 /* Cancel */
- #define EOT 0x04 /* end of text */
-
- /* Menu text tables (Used by 'vmenu') */
- char *main_menu[] = {
- "Terminal Emulation",
- "XMODEM Download",
- "XMODEM Upload",
- "Serial port config",
- "Exit to DOS",
- 0 };
-
- char *setup_menu[] = {
- "Comm port",
- "Baudrate",
- "Data bits",
- "Parity",
- "Stop bits",
- "Xon/Xoff",
- 0 };
-
- /* Uart configuration data tables */
- unsigned baudvalue[] =
- { _110, _300, _1200, _2400, _4800, _9600, _19200, _38400 };
- char *baudtext[] =
- { "110", "300", "1200", "2400", "4800", "9600", "19200", "38400", 0 };
- char *databits[] = { "Five", "Six", "Seven", "Eight", 0 };
- char *parity[] = { "Odd", "Even", "Mark", "Space", "None", 0 };
- char *onetwo[] = { "One", "Two", 0 };
- char *onefour[] = { "One", "Two", "Three", "Four", 0 };
- char *flowctrl[] = { "Disabled", "Enabled", 0 };
-
- /* Communications configuration parameters */
- int comm = 0, baud = 5, data = 3, par = 4, stop = 0, flow = 1;
-
- /* Misc global variables */
- setup_selection = 0, transfer_selection = 0;
- char dfile[FILSIZ+1] = "", ufile[FILSIZ+1] = "";
-
- /* Saved video screens, attributes & cursor position */
- char sav_buffer[(25*80)*2], sav_attr;
- int sav_xy;
- char video_save_area[SCR_BUF];
-
- /*
- * Main terminal program menu
- */
- tty_main()
- {
- int i;
-
- i = 0; /* Default to top of menu */
- save_video(video_save_area);
-
- redraw:
- draw_title();
- vdraw_box(0, SETROW, 79, 2);
- show_settings();
-
- for(;;) {
- message("Select function and press ENTER");
- if(vmenu(MAICOL, MENROW, main_menu, 0, &i))
- continue;
- switch(i) {
- case 0 : /* Terminal Emulation */
- if(!open_comm(flow))
- break;
- vcursor_line();
- restore_screen();
- ansi_term();
- save_screen();
- vcursor_off();
- goto redraw;
- case 1 : /* Download a file */
- if(open_comm(0))
- download(dfile);
- break;
- case 2 : /* Upload a file */
- if(open_comm(0))
- upload(dfile);
- break;
- case 3 : /* Setup serial port */
- setup();
- break;
- case 4 : /* Exit to DOS */
- Cclose();
- restore_video(video_save_area);
- return; } }
- }
-
- /*
- * Open a file for read or write (with overwrite prompt)
- */
- HANDLE openf(fname, rw)
- char *fname, rw;
- {
- char c, omsg[80], *mode;
- HANDLE fh;
-
- mode = "read";
- fh = open(fname, F_READ); /* First try and read the file */
- if(rw) { /* If writing the file */
- mode = "write";
- if(fh) {
- close(fh);
- sprintf(omsg, "Overwrite existing %s (Y/N) ?", fname);
- message(omsg);
- do {
- c = toupper(vgetc());
- if((c == 0x1B) || (c == 'N'))
- return 0; }
- while(c != 'Y'); }
- fh = open(fname, F_WRITE); }
- if(!fh) {
- sprintf(omsg,"Cannot %s %s (Press ENTER)", mode, fname);
- message(omsg);
- while(vgetc() != '\n'); }
- return fh;
- }
-
- /*
- * Open comm port with correct settings
- */
- open_comm(flow)
- char flow;
- {
- int mode;
-
- /* Calculate the communications parameter value */
- mode = ((par << 4) & 0x30) | /* parity type */
- (data & 0x03) | /* # data bits */
- ((stop << 2) & 0x04) | /* # stop bits */
- ((par < 4) << 3); /* parity enable */
-
- /* Open the communications port */
- if(Copen(comm+1, baudvalue[baud], mode, SET_DTR|SET_RTS|OUTPUT_2)) {
- message("Cannot open COM port (Press ENTER)");
- while(vgetc() != '\n');
- return 0; }
-
- /* Remove transparency if XON/XOFF flow control */
- disable();
- Cflags = (flow) ? Cflags & ~TRANSPARENT : Cflags | TRANSPARENT;
- enable();
-
- return -1;
- }
-
- /*
- * Draw the title header
- */
- draw_title()
- {
- vopen();
- V_ATTR = REVERSE;
- vdraw_box(0, 0, 79, 2);
- vgotoxy(1, 1);
- vputf("", 26);
- vputf("MICRO-Terminal Version 1.3", 52);
- V_ATTR = NORMAL;
- vcursor_off();
- }
-
- /*
- * Draw the file transfer information box
- */
- info_box(mode, filename)
- char *mode, *filename;
- {
- vdraw_box(SUBCOL, MENROW+1, 50, 8);
- vgotoxy(SUBCOL+2, MENROW+3);
- vprintf("%-19s: %s", mode, filename);
- vgotoxy(SUBCOL+2, MENROW+5);
- vputs("Blocks transferred : 0");
- vgotoxy(SUBCOL+2, MENROW+7);
- vputs("Transfer status : ");
- message("File transfer in progress (ESCAPE to abort)");
- }
-
- /*
- * Update the transfer status field
- */
- transfer_status(text)
- char *text
- {
- vgotoxy(SUBCOL+23, MENROW+7);
- vputf(text,10);
- }
-
- /*
- * Show the current COM port settings
- */
- show_settings()
- {
- vgotoxy(18, SETROW+1);
- vprintf("COM%u: %5s,%2d,%5s,%2d Xon/Xoff %-8s",
- comm+1, baudtext[baud], data+5, parity[par], stop+1, flowctrl[flow]);
- }
-
- /*
- * Display a message
- */
- message(ptr)
- char *ptr;
- {
- vgotoxy(0, MSGROW);
- vcleos();
- vmessage(38 - strlen(ptr)/2, MSGROW, ptr);
- }
-
- /*
- * Save the MICRO-TERMINAL video screen.
- */
- save_screen()
- {
- sav_xy = V_XY;
- sav_attr = V_ATTR;
- copy_seg(get_ds(), sav_buffer, V_BASE, 0, (25*80)*2);
- }
-
- /*
- * Restore the MICRO-TERMINAL video screen
- */
- restore_screen()
- {
- copy_seg(V_BASE, 0, get_ds(), sav_buffer, (25*80)*2);
- V_ATTR = sav_attr;
- V_XY = sav_xy;
- vupdatexy();
- }
-
- /*
- * Comm port setup menu handler
- */
- setup()
- {
- message("Select setting (ESCAPE to cancel)");
- for(;;) {
- show_settings();
- if(vmenu(SUBCOL, MENROW+1, setup_menu, 0, &setup_selection))
- return;
- switch(setup_selection) {
- case 0 : /* Comm port */
- vmenu(SUBCOL+11,MENROW+2,onefour,-1,&comm);
- break;
- case 1 : /* baudrate */
- vmenu(SUBCOL+11,MENROW+2,baudtext,-1,&baud);
- break;
- case 2 : /* Data bits */
- vmenu(SUBCOL+11,MENROW+2,databits,-1,&data);
- break;
- case 3 : /* Parity */
- vmenu(SUBCOL+11,MENROW+2,parity,-1,&par);
- break;
- case 4 : /* Stop bits */
- vmenu(SUBCOL+11,MENROW+2,onetwo,-1,&stop);
- break;
- case 5 : /* Flow control */
- vmenu(SUBCOL+11,MENROW+2,flowctrl,-1,&flow); } }
- }
-
- /*
- * Terminal mode using ANSI (VT100) emulation
- */
- ansi_term()
- {
- char c, xy_flag, *ptr;
- unsigned x, y, state, value, parm, parms[5];
-
- /* ANSI (VT100) Function key translation table */
- static char *ansi_keys[] = {
- "\x1B[A", "\x1B[B", "\x1B[D", "\x1B[C", /* Arrow keys */
- "\x1BOR", "\x1BOS", "\x1BOP", "\x1BOQ", /* PgUp, Pgdn, Home, End */
- "\x1BOM", "\x1BOm", "\x1BOp", /* Keypad '+','-' Insert */
- "\x7F", "\x08", /* Delete & Backspace */
- "\x1BOq", "\x1BOr", "\x1BOs", "\x1BOt", /* F1, F2, F3 & F4 */
- "\x1BOu", "\x1BOv", "\x1BOw", "\x1BOx", /* F5, F6, F7 & F8 */
- "\x1BOy", "\x1BOz", /* F9 & F10 */
- "\x1BOl", "\x1BOn", 0, 0 }; /* Control: Pgup, Pgdn, Home, End */
-
- xy_flag = -1; /* Force initial cursor update */
- state = 0; /* Not receiving a control sequence */
- for(;;) {
- /* Process any input from the comm port */
- if((c = Ctestc()) != -1) {
- xy_flag = -1;
- if(c == 0x1B) { /* Begin escape sequence */
- state = 1;
- parms[0] = parms[1] = value = parm = 0; }
- else switch(state) {
- case 1 : /* Escape already received */
- if(c == '[') {
- state = 2;
- break; }
- state = 0;
- case 0 : /* No special processing */
- vputc(c);
- break;
- case 2 : /* Waiting for numeric parms */
- if(isdigit(c)) {
- value = (value * 10) + (c - '0');
- break; }
- parms[parm++] = value; /* More to come */
- if(c == ';') {
- value = 0;
- break; }
- state = 0;
- switch(c) {
- case 'H' : /* Cursor position (1) */
- case 'f' : /* Cursor position (2) */
- if(y = parms[0])
- --y;
- if(x = parms[1])
- --x;
- vgotoxy(x, y);
- break;
- case 'J' : /* Erase in display */
- x = V_XY;
- value ? vclscr() : vcleos();
- V_XY = x;
- break;
- case 'K' : /* Erase in line */
- x = V_XY;
- if(value)
- V_XY &= 0xff00;
- vcleol();
- V_XY = x;
- break;
- case 'm' : /* Select attributes */
- x = 0;
- do {
- V_ATTR = (y = parms[x]) ? (y == 4) ?
- UNDERLINE : REVERSE : NORMAL; }
- while(++x < parm); } } }
- else if(xy_flag) { /* Cursor has moved */
- vupdatexy();
- xy_flag = 0; }
-
- /* Process any input from the keyboard */
- if(c = vtstc()) {
- if(c & 0x80) { /* Special function key */
- if(!(ptr = ansi_keys[c & 0x7f]))
- return;
- while(*ptr)
- Cputc(*ptr++); }
- else
- Cputc((c == '\n') ? '\r' : c); } }
- }
-
- /*
- * Receive a file in XMODEM protocol
- */
- download()
- {
- char rbuffer[BLOCK_SIZE], *error_text, *old_error, ochr;
- int r, rx_block_num, error_count;
- HANDLE fh;
-
- if(vgets(FILCOL,MSGROW,"Write to file? ",dfile,FILSIZ) || !*dfile)
- return;
-
- if(!(fh = openf(dfile, -1)))
- return;
-
- info_box("Download to file", dfile);
-
- error_text = old_error = rx_block_num = 1;
- error_count = RETRYS;
- do {
- if(vtstc() == 0x1b) { /* Console escape */
- ochr = CAN;
- error_text = "CANCELED";
- error_count = 0; }
- else if((r = get_record(rbuffer)) == (rx_block_num & 255)) {
- error_count = RETRYS;
- write(rbuffer, BLOCK_SIZE, fh);
- vgotoxy(SUBCOL+23, MENROW+5);
- vprintf("%u", rx_block_num++);
- error_text = "RX PACKET";
- ochr = ACK; }
- else {
- switch(r) {
- case -1 : /* Timeout */
- error_text = "TIMEOUT";
- ochr = NAK;
- break;
- case -2 : /* Bad block */
- error_text = "BAD BLOCK#";
- while(Cgett(RX_TIMEOUT) != -1);
- ochr = NAK;
- break;
- case -3 : /* Bad checksum */
- error_text = "BAD CHKSUM";
- ochr = NAK;
- break;
- case -4 : /* End of file */
- error_text = "DONE";
- ochr = ACK;
- break;
- case -5 : /* Cancel */
- error_text = "ABORTED";
- ochr = ACK;
- break;
- default: /* Block out of sequence */
- error_text = "WRONG BLK";
- ochr = NAK; }
- --error_count; }
- Cputc(ochr);
- /* Update status message */
- if(error_text != old_error)
- transfer_status(old_error = error_text); }
- while((r > -3) && error_count);
-
- message("Download ended (Press ENTER)");
- close(fh);
- while(vgetc() != '\n');
- vclear_box(SUBCOL, MENROW+1, 50, 8);
- }
-
- /*
- * Read a record in the XMODEM protocol, return the block number
- * (0-255) if successful, or one of the following return codes:
- * -1 = Timeout
- * -2 = Bad block number
- * -3 = Bad block checksum
- * -4 = End of file
- * -5 = Canceled by remote
- */
- get_record(rbuffer)
- char rbuffer[];
- {
- int c, i, block_num, check_sum;
-
- check_sum = 0;
- i = -2;
- switch(Cgett(SOH_TIMEOUT)) {
- case SOH : /* Receive packet */
- for(;;) {
- if((c = Cgett(RX_TIMEOUT)) == -1) /* receive timeout */
- break;
- if(i == -2) /* first block number */
- block_num = c;
- else if(i == -1) { /* second block number */
- if((255 & ~c) != block_num)
- return -2; }
- else if(i == BLOCK_SIZE) /* checksum at end */
- return (check_sum & 0xff) == c ? block_num : -3;
- else /* data character */
- check_sum += (rbuffer[i] = c);
- ++i; }
- case -1 : /* timeout on waiting for packet */
- return -1;
- case EOT : /* end of file encountered */
- return -4;
- case CAN : /* cancel protocol */
- return -5; }
- }
-
- /*
- * Transmit a file in XMODEM protocol
- */
- upload()
- {
- int i, c, tx_block_num, error_count;
- char buffer[BLOCK_SIZE], *error_text, *old_error;
- HANDLE fh;
-
- if(vgets(FILCOL,MSGROW,"Read from file? ",ufile,FILSIZ) || !*ufile)
- return;
-
- if(!(fh = openf(ufile, 0)))
- return;
-
- info_box("Upload from file", ufile);
-
- tx_block_num = old_error = error_text = 1;
- error_count = RETRYS;
-
- /* Transmit the file data */
- while(i = read(buffer, BLOCK_SIZE, fh)) {
- while(i < 128)
- buffer[i++] = -1;
- error_text = "TX PACKET";
- while(i = send_record(buffer, tx_block_num)) {
- switch(i) {
- case -1 :
- error_text = "TIMEOUT";
- break;
- case -3 :
- error_text = "RECV NAK";
- break;
- case -5 :
- error_text = "ABORTED"; }
- transfer_status(old_error = error_text);
- if((i < -3) || !error_count--)
- break; }
- if(vtstc() == 0x1b) { /* Console escape */
- i = -5;
- error_text = "CANCELED"; }
- if(i) { /* Error exit */
- Cputc(CAN);
- break; }
- error_count = RETRYS;
- vgotoxy(SUBCOL+23, MENROW+5);
- vprintf("%u", tx_block_num++);
- if(error_text != old_error)
- transfer_status(old_error = error_text); }
-
- /* Send the end of file indicator */
- error_count = RETRYS;
- if(!i) for(;;) {
- Cputc(EOT);
- if((c = Cgett(ACK_TIMEOUT)) == ACK) {
- error_text = "DONE";
- break; }
- if(c == CAN) {
- error_text = "ABORTED";
- break; }
- if(((c == -1) || (c == NAK)) && !error_count--) {
- error_text = "TIMEOUT";
- break; } }
-
- transfer_status(error_text);
- message("Upload ended (Press ENTER)");
- close(fh);
- while(vgetc() != '\n');
- vclear_box(SUBCOL, MENROW+1, 50, 8);
- }
-
- /*
- * Send an record in XMODEM protocol, return 0 if successful
- * Otherwise, return one of the following:
- * -1 = Timeout
- * -2 = Bad block number (N/A)
- * -3 = Bad block (NAK RECEIVED)
- * -4 = End of file (N/A)
- * -5 = Canceled by remote
- */
- send_record(buffer, block_num)
- char *buffer;
- int block_num;
- {
- int i, check_sum;
- char *ptr;
-
- check_sum = 0;
- ptr = buffer;
- while(Ctestc() != -1); /* purge any received data */
- Cputc(SOH);
- Cputc(block_num);
- Cputc(~block_num);
- for(i=0; i < BLOCK_SIZE; ++i) {
- Cputc(*buffer);
- check_sum += *buffer++; }
- Cputc(check_sum);
-
- for(;;) switch(Cgett(ACK_TIMEOUT)) {
- case ACK : /* Packet received ok */
- return 0;
- case NAK : /* Rejected */
- return -3;
- case CAN : /* Remote cancel */
- return -5;
- case -1 : /* Timeout */
- return -1; }
- }
-
- /*
- * Wait for a character from the modem (with timeout)
- */
- Cgett(timeout)
- unsigned timeout;
- {
- int h, m, s, old_s;
-
- if((h = Ctestc()) != -1) /* Very fast if characters buffered */
- return h;
-
- get_time(&h, &m, &old_s);
- do {
- do {
- if((h = Ctestc()) != -1)
- return h;
- get_time(&h,&m,&s); }
- while(s == old_s);
- old_s = s; }
- while(--timeout);
- return -1;
- }
-
- /*
- * Main program, either TSR or execute main tty program menu
- */
- main(argc, argv)
- int argc;
- int *argv[];
- {
- int hot_keys;
- char *ptr;
- vopen();
- vputs("MICRO-Terminal: (Press CTRL-HOME or CTRL-END to exit):\n");
- save_screen();
-
- /* If RAM-resident, print startup message & TSR */
- if(argc > 1) {
- draw_title();
- printf("\n\n\nCopyright 1990-1994 Dave Dunfield\nAll rights reserved.\n");
- vcursor_line();
- hot_keys = 0;
- ptr = argv[1];
- while(*ptr) switch(toupper(*ptr++)) {
- case 'A' : hot_keys |= ALT; break;
- case 'C' : hot_keys |= CONTROL; break;
- case 'L' : hot_keys |= L_SHIFT; break;
- case 'R' : hot_keys |= R_SHIFT; break;
- case 'S' : hot_keys |= SYS_REQ; break;
- default: abort("\nInvalid HOTKEY"); }
- tsr(&tty_main, hot_keys, 2000); }
-
- /* Not RAM-resident, execute the program */
- vclscr(); /* Return to blank screen */
- tty_main();
- }
-